using System;
using System.Collections.Generic;
using System.Text;
using Lidgren.Library.Network;
using Lidgren.Library.Network.Xna;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Game1
{
    public class Client : Network
    {
        private NetAppConfiguration nac;
        public NetClient netClient;
        private NetLog Log;
        public string[][] serverNameIP = new string [Config.Singleton.ServerListLimit][];
        private int msgType; //indica o tipo da mensagem recebida
        public Client()
        {

            nac = new NetAppConfiguration("Game");
            Log = new NetLog();
            Log.IgnoreTypes = NetLogEntryTypes.None;
            Log.IsOutputToFileEnabled = true;
            Log.OutputFileName = "Client.html";

            //We Initiate the client here, it has not connected to the server yet.
            netClient = new NetClient(nac, Log);

            netClient.ServerDiscovered += new EventHandler<NetServerDiscoveredEventArgs>(Client_ServerDiscovered);
            netClient.StatusChanged += new EventHandler<NetStatusEventArgs>(client_StatusChanged);

            for (int i = 0; i < Config.Singleton.ServerListLimit; i++)
            {
                serverNameIP[i] = new string[2];
                serverNameIP[i][0] = null;
                serverNameIP[i][1] = null;
            }
        }

        public void clientConnect(string hostName)
        {
            netClient.Connect(hostName,Config.Singleton.ServerPort);
            this.serverNameIP[0][0]=hostName;
            for (int i = 1; i < Config.Singleton.ServerListLimit; i++)
                {
                serverNameIP[i][0] = null;
                serverNameIP[i][1]= null;
                }
        }
        void client_StatusChanged(object sender, NetStatusEventArgs e)
        {

           if (e.Connection.Status == NetConnectionStatus.Connected)
            {
                NetMessage msg = new NetMessage();
                msg.Write(1);
                XnaSerialization.Write(msg, Entitities.player.model.PhysicsBody.Position);
                XnaSerialization.Write(msg, Entitities.player.model.PhysicsBody.Velocity);
                XnaSerialization.Write(msg, Entitities.player.model.PhysicsBody.AngularVelocity);
                XnaSerialization.WriteMatrix(msg, Entitities.player.model.PhysicsBody.Orientation);
                
                netClient.SendMessage(msg, NetChannel.ReliableUnordered);
            }
            if (e.Connection.Status == NetConnectionStatus.Disconnected)
            {
                netClient.Connect(this.serverNameIP[0][1],Config.Singleton.ServerPort);
            }
        }
        
        public void findLocalServers()
        {
            netClient.DiscoverLocalServers(Config.Singleton.ServerPort);
            netClient.Heartbeat();
            
        }
        
       void Client_ServerDiscovered(object sender,NetServerDiscoveredEventArgs e)
        {
           for (int i = 0; i < Config.Singleton.ServerListLimit; i++)
            {
                if (serverNameIP[i][0] != null)
                {
                    if (serverNameIP[i][0] == e.ServerInformation.ServerName)
                     break;
                }
                else
                {
                    serverNameIP[i][0] = e.ServerInformation.ServerName;
                    serverNameIP[i][1]=e.ServerInformation.RemoteEndpoint.Address.ToString();
                    break;
                }                    
            }
        }
       
        public override void Update()
        {
            while (netClient.Status == NetConnectionStatus.Disconnected)
            {
                netClient.Connect(serverNameIP[0][1], Config.Singleton.ServerPort);
                netClient.Heartbeat();
            }//this line here, Pumps the Session with any packets 
            //that we have received since last time.

            // o usuario envia a informacao 
            NetMessage msgSent = new NetMessage();

            msgSent.Write(0);
            msgSent.Write(Entitities.player.id);
            XnaSerialization.Write(msgSent, Entitities.player.model.PhysicsBody.Position);
            XnaSerialization.Write(msgSent, Entitities.player.model.PhysicsBody.Velocity);
            XnaSerialization.Write(msgSent, Entitities.player.model.PhysicsBody.AngularVelocity);
            XnaSerialization.WriteMatrix(msgSent, Entitities.player.model.PhysicsBody.Orientation);

            netClient.SendMessage(msgSent, NetChannel.ReliableUnordered);
            netClient.Heartbeat();            
            //This will hold our NetMessage, or our packet
            NetMessage msgReceived;
            
            
            //This will be called for every message we have received, 
            //when the message is received we read the message from the packet.
            while ((msgReceived = netClient.ReadMessage()) != null)
            {
                //We will call this routing to handle the message 
                //and process it accordingly.
                HandleMSG(msgReceived);
            }

            

            
        }

        public void HandleMSG(NetMessage msg)
        {
            msg.ResetReadPointer();
            msgType = msg.ReadInt();
            switch(msgType)
            {
                case 0:
                {//durante o jogo normal, update das posicoes de todos
                    //recebendo a posicao dos objetos do servidor
                    foreach (PhysicObject x in Entitities.PhysicObjectList)
                    {
                        x.PhysicsBody.Position = XnaSerialization.ReadVector3(msg);
                        x.PhysicsBody.Velocity = XnaSerialization.ReadVector3(msg);
                        x.PhysicsBody.AngularVelocity = XnaSerialization.ReadVector3(msg);
                        x.PhysicsBody.Orientation = XnaSerialization.ReadMatrix(msg);
                    }
                    //recebendo a posicao dos outros players
                    int id;
                    for (int i = 0; i < Entitities.EnemyList.Count + 1; i++)
                    {
                        id = msg.ReadInt();
                        if (id != Entitities.player.id)
                        {
                            foreach (Enemy x in Entitities.EnemyList)
                            {
                                if (x.id == id)
                                {
                                    x.model.PhysicsBody.Position = XnaSerialization.ReadVector3(msg);
                                    x.model.PhysicsBody.Velocity = XnaSerialization.ReadVector3(msg);
                                    x.model.PhysicsBody.AngularVelocity = XnaSerialization.ReadVector3(msg);
                                    x.model.PhysicsBody.Orientation = XnaSerialization.ReadMatrix(msg);
                                }
                            }
                        }
                        else
                        {
                            //jogando fora a informacao sobre o proprio player
                            //o proprio cliente eh responsavel pelo calculo de sua posicao
                            XnaSerialization.ReadVector3(msg);
                            XnaSerialization.ReadVector3(msg);
                            XnaSerialization.ReadVector3(msg);
                            XnaSerialization.ReadMatrix(msg);
                        }
                    }
                    break;
                }
                case 1:
                {   
                    //logo que o player conecta ele recebe o seu id unico
                    Entitities.player.id = msg.ReadInt();
                    //recebe o estado dos inimigos e tem que criar eles
                    int numEnemys = msg.ReadInt();
                    for(int i=0;i<numEnemys;i++)
                    {
                        //recebe dados dos oponentes
                        int id = msg.ReadInt();
                        Vector3 position = XnaSerialization.ReadVector3(msg);
                        Vector3 velocity = XnaSerialization.ReadVector3(msg);
                        Vector3 angularVelocity = XnaSerialization.ReadVector3(msg);
                        Matrix orientation = XnaSerialization.ReadMatrix(msg);

                        PhysicObject nave = new BoxObject(Base.Content.Load<Model>("Models\\p1_wedge"),
                        new Vector3(2, 1, 2),orientation,position,Entitities.player.model.Camera);
                        nave.Scale = new Vector3(0.001f, 0.001f, 0.001f);

                        Base.Singleton.Components.Add(nave);                                                    
                        Enemy enemy = new Enemy("joao da silva", nave);
                        enemy.id = id;
                        Entitities.EnemyList.Add(enemy);                            
                    }
                    break;
                }
                case 2:
                { //caso algum outro player entre em jogo
                  //recebe-se os dados desse novo player
                  //cria-se entao um novo inimigo
                    int id = msg.ReadInt();
                    if (id != Entitities.player.id)
                    {
                        Vector3 position = XnaSerialization.ReadVector3(msg);
                        Vector3 velocity = XnaSerialization.ReadVector3(msg);
                        Vector3 angularVelocity = XnaSerialization.ReadVector3(msg);
                        Matrix orientation = XnaSerialization.ReadMatrix(msg);


                        PhysicObject nave = new BoxObject(Base.Content.Load<Model>("Models\\p1_wedge"),
                        new Vector3(2, 1, 2), orientation, position, Entitities.player.model.Camera);
                        nave.Scale = new Vector3(0.001f, 0.001f, 0.001f);

                        Base.Singleton.Components.Add(nave);
                        Enemy enemy = new Enemy("joao da silva", nave);
                        enemy.id = id;
                        Entitities.EnemyList.Add(enemy);
                    }
                    else
                    {
                        //jogando fora a informacao sobre o proprio player
                        //o proprio cliente eh responsavel pelo calculo de sua posicao
                        XnaSerialization.ReadVector3(msg);
                        XnaSerialization.ReadVector3(msg);
                        XnaSerialization.ReadVector3(msg);
                        XnaSerialization.ReadMatrix(msg);
                    }
                    break;
                }           
                
            }
        }
    }
}

